package com.joysuch.wwyt.bp.service.impl;

import cn.hutool.core.date.DateUtil;
import cn.hutool.core.io.IoUtil;
import cn.hutool.poi.excel.ExcelUtil;
import cn.hutool.poi.excel.ExcelWriter;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.google.common.collect.Lists;
import com.joysuch.wwyt.api.dataaccess.OrientationDataSyncService;
import com.joysuch.wwyt.bp.bean.BpInspectExportBean;
import com.joysuch.wwyt.bp.bean.inspect.*;
import com.joysuch.wwyt.bp.entity.inspect.*;
import com.joysuch.wwyt.bp.repository.*;
import com.joysuch.wwyt.bp.service.BPInspectPointService;
import com.joysuch.wwyt.bp.service.BpInspectPlanService;
import com.joysuch.wwyt.bp.service.BpInspectRecordService;
import com.joysuch.wwyt.common.entity.CommonIndexNotify;
import com.joysuch.wwyt.common.enums.CommonIndexNotifyTypes;
import com.joysuch.wwyt.common.repository.CommonIndexNotifyDao;
import com.joysuch.wwyt.core.common.ajax.ResultBean;
import com.joysuch.wwyt.core.common.util.PageDataUtils;
import com.joysuch.wwyt.core.entity.BaseUser;
import com.joysuch.wwyt.core.repository.BaseUserDao;
import com.joysuch.wwyt.core.security.ShiroUser;
import com.joysuch.wwyt.core.support.Context;
import com.joysuch.wwyt.edu.bean.EduExamReleaseDto;
import com.joysuch.wwyt.util.FileUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.shiro.util.Assert;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageImpl;
import org.springframework.data.domain.Pageable;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.data.querydsl.QPageRequest;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;

import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.Predicate;
import javax.persistence.criteria.Root;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.OutputStream;
import java.time.*;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.concurrent.Future;
import java.util.concurrent.ScheduledThreadPoolExecutor;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.function.Function;
import java.util.stream.Collectors;
import java.util.stream.Stream;

@Slf4j
@Service
@Transactional(rollbackFor = Exception.class)
public class BpInspectRecordServiceImpl implements BpInspectRecordService {

    @Autowired
    private BpInspectRecordRepository bpInspectRecordRepository;
    @Autowired
    private BpInspectRecordDetailRepository bpInspectRecordDetailRepository;
    @Autowired
    private BpInspectTypeRepository bpInspectTypeRepository;
    @Autowired
    private BaseUserDao baseUserDao;
    @Autowired
    private BpInspectPlanService bpInspectPlanService;
    @Autowired
    private BPInspectPointService bpInspectPointService;
    @Autowired
    private BpInspectPlanRepository bpInspectPlanRepository;
    @Autowired
    private BpDeviceFacilityCollectionDao bpDeviceFacilityCollectionDao;
    @Autowired
    private BpInspectRecordPointRepository bpInspectRecordPointRepository;
    @Autowired
    private BpAppPlanTaskRepository bpAppPlanTaskRepository;
    @Autowired
    private CommonIndexNotifyDao commonIndexNotifyDao;
    private static final Integer STACK_LIMIT = 5000;
    private static final Integer STACK_LIMIT_SPLIT = 2500;
    @Autowired
    private BPInspectPointRepository pointRepository;
    @Autowired
    private OrientationDataSyncService orientationDataSyncService;
    private static final ScheduledThreadPoolExecutor executor = new ScheduledThreadPoolExecutor(5, r -> {
        Thread thread = new Thread(r, "inspect record process worker");
        thread.setDaemon(true);
        return thread;
    });

    @Override
    public ResultBean save(BpInspectRecordBean bean, Integer flag) {
        Long appPlanTaskId = bean.getAppPlanTaskId();
        if (flag == 1) {
            Assert.notNull(appPlanTaskId, "appPlanTaskId不能为空");
            List<BpInspectRecord> records = bpInspectRecordRepository.findByAppPlanTaskId(appPlanTaskId);
            if (!CollectionUtils.isEmpty(records)) {
                return ResultBean.fail(101, "重复提交");
            }
            //一个任务分给多个人，只需要一个人做
            //前面都是未检的记录都生成，直到遇到做了的记录，把前面所有未检的删除
            //前面有做了的记录，1.再进来的未检都不生成记录 2.再进来的做了的记录继续生成
            //只要有一个完成，需要将整个任务都设置成已完成
            //找到所有相同任务
            BpAppPlanTask bpAppPlanTask = bpAppPlanTaskRepository.getOne(appPlanTaskId);
            if (bpAppPlanTask.getMultiUserTaskFlag() != null && bpAppPlanTask.getMultiUserTaskFlag() == 1) {
                Integer taskId = bpAppPlanTask.getTaskId();
                List<BpAppPlanTask> tempList = bpAppPlanTaskRepository.findByTaskIdAndIdNot(taskId, bpAppPlanTask.getId());
                //同一任务下的协作任务
                tempList = tempList.stream().filter(r -> r.getCheckDate().equals(bpAppPlanTask.getCheckDate()) &&
                        r.getCheckTime().equals(bpAppPlanTask.getCheckTime())).collect(Collectors.toList());
                List<Long> appPlanTaskIds = tempList.stream().map(r -> r.getId()).collect(Collectors.toList());
                if (!CollectionUtils.isEmpty(appPlanTaskIds)) {
                    List<BpInspectRecord> recordList = bpInspectRecordRepository.findByAppPlanTaskIdIn(appPlanTaskIds);
                    List<BpInspectRecord> unCheckRecordList = recordList.stream().filter(r -> "任务未检".equals(r.getUncheckStatus()))
                            .collect(Collectors.toList());
                    //未检
                    if (bean.getUncheckFlag() == 1) {
                        bpAppPlanTask.setStatus("未完成");
                        //协作任务里有已完成时，本任务状态：已完成
                        long count = tempList.stream().filter(r -> "已完成".equals(r.getStatus())).count();
                        if (count > 0) {
                            bpAppPlanTask.setStatus("已完成");
                            bpAppPlanTaskRepository.save(bpAppPlanTask);
                            return ResultBean.defaultSuccessResult();
                        }
                        bpAppPlanTaskRepository.save(bpAppPlanTask);
                    } else {
                        if (!CollectionUtils.isEmpty(unCheckRecordList)) {
                            for (BpInspectRecord bpInspectRecord : unCheckRecordList) {
                                bpInspectRecordRepository.deleteById(bpInspectRecord.getId());
                            }
                        }
                        //已完成，则其余任务都置为已完成
                        tempList.forEach(r -> r.setStatus("已完成"));
                        bpAppPlanTaskRepository.saveAll(tempList);
                    }
                }
            }
        }
        Long inspectPlanId = bean.getInspectPlanId();
        LocalDateTime inspectStartTime = bean.getInspectStartTime();
        LocalDateTime inspectFinishTime = bean.getInspectFinishTime();
        if (checkTime(inspectStartTime, inspectFinishTime)) return ResultBean.fail(101, "请填写正确的检查开始/结束时间");
        List<BpInspectRecordDetailBean> recordDetails = bean.getRecordDetails();
        recordDetails = recordDetails == null ? Lists.newArrayList() : recordDetails;
        for (BpInspectRecordDetailBean recordDetail : recordDetails) {
            LocalDateTime inspectStartTime1 = recordDetail.getInspectStartTime();
            LocalDateTime inspectFinishTime1 = recordDetail.getInspectFinishTime();
            if (checkTime(inspectStartTime1, inspectFinishTime1)) return ResultBean.fail(101, "请填写正确的检查开始/结束时间");
        }
        Long id = bean.getId();
        BpInspectRecord bpInspectRecord;
        if (id == null) {
            bpInspectRecord = new BpInspectRecord();
            bpInspectRecord.setUploadWay(flag == 1 ? "app" : "web");
        } else {
            bpInspectRecord = bpInspectRecordRepository.findById(id).orElse(new BpInspectRecord());
        }
        BeanUtils.copyProperties(bean, bpInspectRecord);
        //判断过期
        if (flag == 1) {
            if (bean.getUncheckFlag() == 0) {
                Assert.notNull(inspectStartTime, "app端检查开始时间必填");
                Assert.notNull(inspectFinishTime, "app端检查结束时间必填");
            }
            expireJudge(inspectPlanId, inspectStartTime, appPlanTaskId, bpInspectRecord, bean.getUncheckFlag());
        }
        BpInspectRecord record = bpInspectRecordRepository.save(bpInspectRecord);
        Long recordId = record.getId();

        for (BpInspectRecordDetailBean bpInspectRecordDetailBean : recordDetails) {
            Long inspectRecordPointId = bpInspectRecordDetailBean.getInspectRecordPointId();
            BpInspectRecordPoint bpInspectRecordPoint;
            if (inspectRecordPointId == null) {
                bpInspectRecordPoint = new BpInspectRecordPoint();
            } else {
                bpInspectRecordPoint = bpInspectRecordPointRepository
                        .findById(bpInspectRecordDetailBean.getInspectRecordPointId()).orElse(new BpInspectRecordPoint());
            }
            BeanUtils.copyProperties(bpInspectRecordDetailBean, bpInspectRecordPoint);
            bpInspectRecordPoint.setId(bpInspectRecordDetailBean.getInspectRecordPointId());
            bpInspectRecordPoint.setInspectRecordId(recordId);
            //每个检查点开始/结束时间表
            bpInspectRecordPointRepository.save(bpInspectRecordPoint);
            Long inspectPointId = bpInspectRecordDetailBean.getInspectPointId();
            //存记录所含项目表
            List<BpInspectRecordPointDetailBean> pointDetails = bpInspectRecordDetailBean.getPointDetails();
            if (CollectionUtils.isEmpty(pointDetails)) {
                continue;
            }
            for (BpInspectRecordPointDetailBean pointDetail : pointDetails) {
                Long inspectRecordDetailId = pointDetail.getInspectRecordDetailId();
                BpInspectRecordDetail bpInspectRecordDetail;
                if (inspectRecordDetailId == null) {
                    bpInspectRecordDetail = new BpInspectRecordDetail();
                } else {
                    bpInspectRecordDetail = bpInspectRecordDetailRepository
                            .findById(pointDetail.getInspectRecordDetailId()).orElse(new BpInspectRecordDetail());
                }
                BeanUtils.copyProperties(pointDetail, bpInspectRecordDetail);
                //记录包含的检查项目详情
                setRecordItemData(inspectPlanId, inspectPointId, pointDetail, bpInspectRecordDetail, record);
                bpInspectRecordDetailRepository.save(bpInspectRecordDetail);
            }
        }
        String checkResult = record.getCheckResult();
        if (checkResult == null) {
            record.setCheckResult("正常");
            bpInspectRecordRepository.save(record);
        }
        //异常通知:选择部门，并在出现巡检状态为‘异常’“超时”等不正常情况时，自动发送通知到首页【通知提醒】模块，
        //发送的模板为：丁二烯储罐区巡检发生超时/异常/未检。公式为：“巡检计划名称”发生“异常类型”。
        exceptionNotify(record, inspectPlanId);

        //app端 检查任务完成 作处理
        if (flag == 1) {
            bpInspectPlanService.checkFinish(appPlanTaskId, bean.getUncheckFlag(), recordId);
        }
        return ResultBean.success(recordId);
    }

    private void exceptionNotify(BpInspectRecord record, Long inspectPlanId) {
        String checkResult = record.getCheckResult();
        if (checkResult == null) {
            return;
        }
        BpInspectPlan bpInspectPlan = bpInspectPlanRepository.findById(inspectPlanId).get();
        String planName = bpInspectPlan.getName();
        Long notifyDepartId = bpInspectPlan.getNotifyDepartId();
        List<Long> userIdList = notifyDepartId == null ? Lists.newArrayList() : baseUserDao.findIdByDepartId(notifyDepartId);
        String[] split = checkResult.split(",");
        List<CommonIndexNotify> needSaveList = Lists.newArrayList();
        for (String result : split) {
            if ("正常".equals(result)) {
                continue;
            }
            for (Long userId : userIdList) {
                CommonIndexNotify notify = new CommonIndexNotify();
                notify.setType(CommonIndexNotifyTypes.INSPECT_TASK.getType());
                notify.setEduCertUserId(record.getId());
                notify.setRemindDate(LocalDate.now());
                notify.setRemindDateTime(LocalDateTime.now());
                notify.setUserId(userId);
                //提醒message格式：“巡检计划名称”发生“异常类型”。
                String Message = planName + "发生" + result;
                notify.setNotifyMessage(Message);
                needSaveList.add(notify);
            }
        }
        if (!CollectionUtils.isEmpty(needSaveList)) {
            commonIndexNotifyDao.saveAll(needSaveList);
        }
    }

    private void setRecordItemData(Long inspectPlanId, Long inspectPointId, BpInspectRecordPointDetailBean pointDetail,
                                   BpInspectRecordDetail bpInspectRecordDetail, BpInspectRecord record) {
        Long recordId = record.getId();
        bpInspectRecordDetail.setId(pointDetail.getInspectRecordDetailId());
        bpInspectRecordDetail.setInspectDeviceId(pointDetail.getInspectDeviceId());
        bpInspectRecordDetail.setInspectRecordId(recordId);
        bpInspectRecordDetail.setInspectPlanId(inspectPlanId);
        bpInspectRecordDetail.setInspectPointId(inspectPointId);
        String[] inspectDevicePics = pointDetail.getInspectDevicePics();
        if (inspectDevicePics != null) {
            bpInspectRecordDetail.setInspectDevicePicture(JSON.toJSONString(inspectDevicePics));
        } else {
            bpInspectRecordDetail.setInspectDevicePicture(JSON.toJSONString(Lists.newArrayList()));
        }

        if ("异常".equals(bpInspectRecordDetail.getInspectResultStatus())) {
            bpInspectRecordDetail.setRepairStatus("未整改");
            setRecordResult(record, "异常");
        } else if ("未检".equals(bpInspectRecordDetail.getInspectResultStatus())) {
            bpInspectRecordDetail.setRepairStatus("无需整改");
            setRecordResult(record, "未检");
        } else {
            bpInspectRecordDetail.setRepairStatus("无需整改");
        }
    }

    private void setRecordResult(BpInspectRecord record, String result) {
        String checkResult = record.getCheckResult();
        if (checkResult == null) {
            record.setCheckResult(result);
        } else {
            if (!checkResult.contains(result)) {
                record.setCheckResult(checkResult + "," + result);
            }
        }
    }

    private void expireJudge(Long inspectPlanId, LocalDateTime inspectStartTime, Long appPlanTaskId,
                             BpInspectRecord bpInspectRecord, Integer uncheckFlag) {
        //如果app端严格控制任务的开始时间，理论上不会存在开始时间不正确的情况，理论上只会存在过期没做的
        BpInspectPlan bpInspectPlan = bpInspectPlanRepository.findById(inspectPlanId).get();
//        String permitDeviationTime = bpInspectPlan.getPermitDeviationTime();
//        int deviationTime = Integer.parseInt(permitDeviationTime.substring(0, permitDeviationTime.length() - 2));
        BpAppPlanTask bpAppPlanTask = bpAppPlanTaskRepository.findById(appPlanTaskId).get();
//        LocalDateTime needStartTime = LocalDateTime.of(bpAppPlanTask.getCheckDate(), bpAppPlanTask.getCheckTime());
//        Duration between = Duration.between(needStartTime, inspectStartTime);
//        long minutes = between.toMinutes();
//        Integer expireResult = minutes > deviationTime ? 1 : 0;
//        bpInspectRecord.setExpireFlag(expireResult);
        if (uncheckFlag == 1) {
            //bpInspectRecord.setExpireFlag(1);
            setRecordResult(bpInspectRecord, "未检");
            bpInspectRecord.setUncheckStatus("任务未检");
        }
        //结束时间的超时判断：
        //if (uncheckFlag == 0) {
        endTimeLimitJudge(bpInspectRecord, bpInspectPlan, bpAppPlanTask, uncheckFlag);
        //}
        if (bpInspectRecord.getExpireFlag() == 1) {
            setRecordResult(bpInspectRecord, "超时");
        }
    }

    /**
     * 去除检查时限
     * 检查超时由后端判断
     * 1.有允许误差时间时:app端限制开始时间(时间前移--为了让早来的人有事做)，范围是[needTime-允许误差时间,+@@]
     * 2.无-------------:app端限制开始时间为任务[needTime,+@@]
     * 后端做结束时间是否超过下个任务needTime的超时判断
     */
    private void endTimeLimitJudge(BpInspectRecord bpInspectRecord, BpInspectPlan bpInspectPlan, BpAppPlanTask bpAppPlanTask, Integer expireResult) {
        LocalDateTime inspectFinishTime = bpInspectRecord.getInspectFinishTime();
        LocalDateTime needTime = LocalDateTime.of(bpAppPlanTask.getCheckDate(), bpAppPlanTask.getCheckTime());
        if ("按周期".equals(bpInspectPlan.getPeriodType())) {
            Integer minuteDuration = bpInspectPlan.getMinuteDuration();
            if (minuteDuration != null && inspectFinishTime.isAfter(needTime.plusMinutes(minuteDuration))) {
                bpInspectRecord.setExpireFlag(1);
            }
        } else {
            String periodStartTime = bpInspectPlan.getPeriodStartTime();
            LocalTime startTime = getStartTime(periodStartTime);
            String periodSetting = bpInspectPlan.getPeriodSetting();
            if ("每天".equals(periodSetting)) {
                LocalDateTime time = LocalDateTime.of(bpAppPlanTask.getCheckDate(), startTime).plusDays(1);
                if (inspectFinishTime.isAfter(time)) {
                    bpInspectRecord.setExpireFlag(1);
                }
            } else if ("每月".equals(periodSetting)) {
                LocalDateTime time = LocalDateTime.of(bpAppPlanTask.getCheckDate(), startTime).plusMonths(1);
                if (inspectFinishTime.isAfter(time)) {
                    bpInspectRecord.setExpireFlag(1);
                }
            } else if ("每年".equals(periodSetting)) {
                LocalDateTime time = LocalDateTime.of(bpAppPlanTask.getCheckDate(), startTime).plusYears(1);
                if (inspectFinishTime.isAfter(time)) {
                    bpInspectRecord.setExpireFlag(1);
                }
            }
        }
//        if (expireResult == 0) {
//            Integer checkLimit = bpInspectPlan.getCheckLimit();
//            LocalDateTime needTime = LocalDateTime.of(bpAppPlanTask.getCheckDate(), bpAppPlanTask.getCheckTime());
//            //有检查时限
//            if (checkLimit != null) {
//                if (LocalDateTime.now().isAfter(needTime.plusMinutes(checkLimit))) {
//                    bpInspectRecord.setExpireFlag(1);
//                }
//            } else {
//                if ("按周期".equals(bpInspectPlan.getPeriodType())) {
//                    Integer minuteDuration = bpInspectPlan.getMinuteDuration();
//                    if (minuteDuration != null && LocalDateTime.now().isAfter(needTime.plusMinutes(minuteDuration))) {
//                        bpInspectRecord.setExpireFlag(1);
//                    }
//                } else {
//                    String periodStartTime = bpInspectPlan.getPeriodStartTime();
//                    LocalTime startTime = getStartTime(periodStartTime);
//                    String periodSetting = bpInspectPlan.getPeriodSetting();
//                    if ("每天".equals(periodSetting)) {
//                        LocalDateTime time = LocalDateTime.of(bpAppPlanTask.getCheckDate(), startTime).plusDays(1);
//                        if (LocalDateTime.now().isAfter(time)) {
//                            bpInspectRecord.setExpireFlag(1);
//                        }
//                    } else if ("每月".equals(periodSetting)) {
//                        LocalDateTime time = LocalDateTime.of(bpAppPlanTask.getCheckDate(), startTime).plusMonths(1);
//                        if (LocalDateTime.now().isAfter(time)) {
//                            bpInspectRecord.setExpireFlag(1);
//                        }
//                    } else if ("每年".equals(periodSetting)) {
//                        LocalDateTime time = LocalDateTime.of(bpAppPlanTask.getCheckDate(), startTime).plusYears(1);
//                        if (LocalDateTime.now().isAfter(time)) {
//                            bpInspectRecord.setExpireFlag(1);
//                        }
//                    }
//                }
//            }
//        }
    }

    private LocalTime getStartTime(String periodStartTime) {
        if (periodStartTime.contains(" ")) {
            String[] split = periodStartTime.split("\\s");
            return LocalTime.parse(split[1], DateTimeFormatter.ofPattern("HH:mm"));
        } else {
            return LocalTime.parse(periodStartTime, DateTimeFormatter.ofPattern("HH:mm"));
        }
    }

    private boolean checkTime(LocalDateTime inspectStartTime, LocalDateTime inspectFinishTime) {
        if (inspectStartTime != null && inspectFinishTime != null && inspectFinishTime.isBefore(inspectStartTime)) {
            return true;
        }
        return false;
    }

    @Override
    public void deleteById(Long id) {
        //记录表删除
        bpInspectRecordRepository.deleteById(id);
        //记录point表删除
        bpInspectRecordPointRepository.deleteByInspectRecordId(id);
        //记录detail表删除
        bpInspectRecordDetailRepository.deleteByInspectRecordId(id);
    }

    //flag: 0 web; 1 app(本人的巡检记录)
    @Override
    public Page<BpInspectRecord> list(BpInspectRecordQueryBean bean, Integer flag) {
        QPageRequest request = QPageRequest.of(bean.getPage(), bean.getSize());
        Pageable pageable = PageDataUtils.addOrderByDesc(request, "createTime");
        String name = bean.getName();
        LocalDate checkDate = bean.getCheckDate();
        String checkResult = bean.getCheckResult();
        AtomicBoolean isEmptyResult = new AtomicBoolean(false);
        Specification<BpInspectRecord> specification = (Root<BpInspectRecord> root, CriteriaQuery<?> criteriaQuery, CriteriaBuilder cb) -> {
            List<Predicate> predicates = new ArrayList<>();
            if (name != null) {
                List<Long> planIdList = bpInspectPlanRepository.findIdByNameLike(name);
                CriteriaBuilder.In<Object> planIdIn = cb.in(root.get("inspectPlanId"));
                for (Long aLong : planIdList) {
                    planIdIn.value(aLong);
                }
                List<Long> userIdList = baseUserDao.getUserIdByNameContains(name);
                CriteriaBuilder.In<Object> userIdIn = cb.in(root.get("inspectUserId"));
                for (Long aLong : userIdList) {
                    userIdIn.value(aLong);
                }
                if (planIdList.size() != 0 && userIdList.size() == 0) {
                    predicates.add(planIdIn);
                } else if (planIdList.size() != 0 && userIdList.size() != 0) {
                    predicates.add(cb.or(planIdIn, userIdIn));
                } else if (planIdList.size() == 0 && userIdList.size() != 0) {
                    predicates.add(userIdIn);
                }else if (planIdList.size() == 0 && userIdList.size() == 0) {
                    // 传入name参数，未匹配到计划或人名。将返回空列表
                    isEmptyResult.set(true);
                    return null;
                }
            }
            if (checkResult != null) {
                predicates.add(cb.like(root.get("checkResult"), "%" + checkResult + "%"));
            }
            if (checkDate != null) {
                LocalDateTime startTime = LocalDateTime.of(checkDate, LocalTime.of(0, 0, 0));
                LocalDateTime endTime = LocalDateTime.of(checkDate, LocalTime.of(23, 59, 59));
                predicates.add(cb.between(root.get("inspectFinishTime"), startTime, endTime));
            }
            if (flag == 1) {
                predicates.add(cb.equal(root.get("inspectUserId"), Context.getCurrentUserId()));
            }
            return cb.and(predicates.toArray(new Predicate[predicates.size()]));
        };
        if (isEmptyResult.get()) {
            return new PageImpl<>(Lists.newArrayList(), pageable, 0);
        }
        Page<BpInspectRecord> all = bpInspectRecordRepository.findAll(specification, pageable);
        if (all.getTotalElements() == 0) {
            return new PageImpl<>(Lists.newArrayList(), pageable, 0);
        }
        pageListHandle(all, true, true);
        List<BpInspectRecord> collect = all.stream().sorted(Comparator.comparing(BpInspectRecord::getId).reversed()).collect(Collectors.toList());
        return new PageImpl<>(collect, pageable, all.getTotalElements());
    }

    /**
     * 处理记录数据: 设置记录异常总数，计划开始时间、检查人员、计划名称等(web分页)
     */
    private void pageListHandle(Iterable<BpInspectRecord> all, boolean needHandleDetail, boolean needPlanStartTime) {
        List<Long> taskIds = Lists.newArrayList();
        List<Long> planIds = Lists.newArrayList();
        List<Long> userIds = Lists.newArrayList();
        List<Long> recordIds = Lists.newArrayList();
        all.forEach(r -> {
            if (r != null) {
                recordIds.add(r.getId());
                Long inspectPlanId = r.getInspectPlanId();
                if (inspectPlanId != null && !planIds.contains(inspectPlanId)) {
                    planIds.add(inspectPlanId);
                }
                Long inspectUserId = r.getInspectUserId();
                if (inspectUserId != null && !userIds.contains(inspectUserId)) {
                    userIds.add(inspectUserId);
                }
            }
        });
        List<BpInspectPlan> planList = bpInspectPlanRepository.findAllById(planIds);
        Map<Long, BpInspectPlan> planMap = planList.stream().collect(Collectors.toMap(BpInspectPlan::getId, Function.identity()));
        List<Long> typeIds = planList.stream().map(r -> r.getInspectTypeId()).distinct().collect(Collectors.toList());
        Map<Long, String> typeMap = bpInspectTypeRepository.findAllById(typeIds).stream().collect(Collectors.toMap(BpInspectType::getId, BpInspectType::getName));
        Map<Long, String> userMap = baseUserDao.findAllById(userIds).stream().collect(Collectors.toMap(BaseUser::getId, BaseUser::getRealName));
        //统计中心，5000条以下stack1M无压力
        if (recordIds.size() > STACK_LIMIT) {
            ShiroUser currentUser = Context.getCurrentUser();
            int totalSize = (recordIds.size() / STACK_LIMIT_SPLIT) + 1;
            for (int j = 0; j < totalSize; j++) {
                List<Long> tempRecordIds = splitRecordIds(recordIds, totalSize, j);
                asyncRecordDetail(all, currentUser, tempRecordIds);
            }
            //record无detail数据设异常总数默认值0
            for (BpInspectRecord record : all) {
                record.setExceptionSum(record.getExceptionSum() == null ? 0 : record.getExceptionSum());
                record.setInspectTotalTime(getTotalTime(record));
            }
            return;
        }
        //web分页
        webPageData(all, needHandleDetail, needPlanStartTime, taskIds, recordIds, planMap, typeMap, userMap);
    }

    private void webPageData(Iterable<BpInspectRecord> all, boolean needHandleDetail, boolean needPlanStartTime, List<Long> taskIds, List<Long> recordIds, Map<Long, BpInspectPlan> planMap, Map<Long, String> typeMap, Map<Long, String> userMap) {
        List<BpInspectRecordDetailDto> detailDtoList = bpInspectRecordDetailRepository.findResultStatusByInspectRecordIdIn(recordIds);
        Map<Long, List<BpInspectRecordDetailDto>> recordDetailMap = detailDtoList.stream()
                .collect(Collectors.groupingBy(BpInspectRecordDetailDto::getId));
        for (BpInspectRecord bpInspectRecord : all) {
            if (bpInspectRecord == null) {
                continue;
            }
            Long appPlanTaskId = bpInspectRecord.getAppPlanTaskId();
            if (appPlanTaskId != null) {
                taskIds.add(appPlanTaskId);
            }

            BpInspectPlan bpInspectPlan = planMap.get(bpInspectRecord.getInspectPlanId());
            //检查名称
            bpInspectRecord.setInspectPlanName(bpInspectPlan != null ? bpInspectPlan.getName() : "");
            if (bpInspectPlan != null) {
                //检查类型
                bpInspectRecord.setInspectType(typeMap.get(bpInspectPlan.getInspectTypeId()));
                //巡检人员 产品：这边是巡检记录新增的检查人员
                String inspectUserName = userMap.get(bpInspectRecord.getInspectUserId());
                bpInspectRecord.setInspectUserName(inspectUserName);
            }

            //todo 巡检过程 暂时不做

            //异常总数
            List<BpInspectRecordDetailDto> resultStatusList = recordDetailMap.get(bpInspectRecord.getId());
            int exceptionSum = 0;
            if (resultStatusList != null) {
                List<String> statusList = resultStatusList.stream().map(r -> r.getInspectResultStatus()).collect(Collectors.toList());
                exceptionSum = calExceptionSum(bpInspectRecord, statusList);
            }
            bpInspectRecord.setExceptionSum(exceptionSum);

            //异常处理：只要将异常的送到整改流程即为已处理 ; 没有异常 状态为无需处理; 部分未处理: 等待处理
            //加逻辑：异常里新增了未检，如果全是未检状态为无需处理;
            setExceptionHandle(needHandleDetail, bpInspectRecord, resultStatusList, exceptionSum);
            //巡检用时
            bpInspectRecord.setInspectTotalTime(getTotalTime(bpInspectRecord));
        }
        setPlanStartTime(all, needPlanStartTime, taskIds);
    }

    /**
     * 显示格式：
     * 0时0分0秒 显示 为 “---”
     * 0时0分X秒 显示 为 “X秒”
     * 0时x分x秒 显示 为 “X分钟”
     * x时x分x秒  显示 为 “X小时X分钟”
     */
    private String getTotalTime(BpInspectRecord record) {
        LocalDateTime inspectStartTime = record.getInspectStartTime();
        LocalDateTime inspectFinishTime = record.getInspectFinishTime();
        String totalTime = "---";
        if (inspectStartTime != null && inspectFinishTime != null) {
            Duration duration = Duration.between(inspectStartTime, inspectFinishTime);
            long seconds = duration.getSeconds();
            long hours = seconds / 3600;
            long minutes = seconds / 60;
            if (hours != 0) {
                long tempSeconds = seconds % 3600;
                minutes = tempSeconds / 60;
                return hours + "小时" + minutes + "分钟";
            }
            if (minutes != 0) {
                return minutes + "分钟";
            }
            if (seconds != 0) {
                return seconds + "秒";
            }
        }
        return totalTime;
    }

    private List<Long> splitRecordIds(List<Long> recordIds, int totalSize, int j) {
        List<Long> tempRecordIds;
        if (j == totalSize - 1) {
            tempRecordIds = recordIds.subList(STACK_LIMIT_SPLIT * j, recordIds.size());
        } else {
            tempRecordIds = recordIds.subList(STACK_LIMIT_SPLIT * j, STACK_LIMIT_SPLIT * (j + 1));
        }
        return tempRecordIds;
    }

    /**
     * 计算异常总数
     */
    private int calExceptionSum(BpInspectRecord bpInspectRecord, List<String> resultStatusList) {
        int exceptionSum = 0;
        //加上未检数量
        if (!CollectionUtils.isEmpty(resultStatusList)) {
            exceptionSum = Long.valueOf(resultStatusList.stream().filter(r -> "异常".equals(r) ||
                    "未检".equals(r)).count()).intValue();
        }
        bpInspectRecord.setExceptionSum(exceptionSum);
        return exceptionSum;
    }

    private void asyncRecordDetail(Iterable<BpInspectRecord> all, ShiroUser currentUser, List<Long> recordIdList) {
        Future<?> submit = executor.submit(() -> {
            Context.setCurrentUser(currentUser);
            List<BpInspectRecordDetailDto> detailDtoList = bpInspectRecordDetailRepository
                    .findResultStatusByInspectRecordIdIn(recordIdList);
            Map<Long, List<String>> recordDetailMap = detailDtoList.stream()
                    .collect(Collectors.groupingBy(BpInspectRecordDetailDto::getId,
                            Collectors.mapping(r -> r.getInspectResultStatus(), Collectors.toList())));

            for (BpInspectRecord record : all) {
                //异常总数
                List<String> resultStatusList = recordDetailMap.get(record.getId());
                if (resultStatusList == null) {
                    continue;
                }
                calExceptionSum(record, resultStatusList);
            }
        });
        try {
            submit.get();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    private void setPlanStartTime(Iterable<BpInspectRecord> all, boolean needPlanStartTime, List<Long> taskIds) {
        if (needPlanStartTime) {
            Map<Long, BpAppPlanTask> map = bpAppPlanTaskRepository.findAllById(taskIds).stream().collect(Collectors.toMap(BpAppPlanTask::getId, Function.identity()));
            for (BpInspectRecord bpInspectRecord : all) {
                Long appPlanTaskId = bpInspectRecord.getAppPlanTaskId();
                BpAppPlanTask bpAppPlanTask = map.get(appPlanTaskId);
                if (bpAppPlanTask != null) {
                    bpInspectRecord.setPlanStartTime(LocalDateTime.of(bpAppPlanTask.getCheckDate(), bpAppPlanTask.getCheckTime()));
                }
            }
        }
    }

    private void setExceptionHandle(boolean needHandleDetail, BpInspectRecord bpInspectRecord,
                                    List<BpInspectRecordDetailDto> resultStatusList, int exceptionSum) {
        if (needHandleDetail) {
            if (exceptionSum == 0) {
                bpInspectRecord.setExceptionHandle("无需处理");
            } else {
                long noCheckCount = resultStatusList.stream().filter(r -> "未检".equals(r.getInspectResultStatus())).count();
                if (noCheckCount == exceptionSum) {
                    bpInspectRecord.setExceptionHandle("无需处理");
                } else {
                    long count = resultStatusList.stream().filter(r -> "异常".equals(r.getInspectResultStatus())
                            && "未整改".equals(r.getRepairStatus())).count();
                    if (count == 0) {
                        bpInspectRecord.setExceptionHandle("已处理");
                    } else {
                        bpInspectRecord.setExceptionHandle("等待处理");
                    }
                }
            }
        }
    }

    @Override
    public ResultBean getById(Long id) {
        BpInspectRecord bpInspectRecord = bpInspectRecordRepository.findById(id).orElse(null);
        if (bpInspectRecord == null) {
            log.warn("检查记录表数据丢失，id：{}", id);
            return ResultBean.fail(101, "该数据已被删除");
        }
        BpInspectRecordCheckDetailDto resultDto = new BpInspectRecordCheckDetailDto();
        BeanUtils.copyProperties(bpInspectRecord, resultDto);
        BpInspectPlan bpInspectPlan = bpInspectPlanService.getById(bpInspectRecord.getInspectPlanId());
        //检查名称
        resultDto.setInspectPlanName(bpInspectPlan != null ? bpInspectPlan.getName() : "");
        //检查人员姓名
        String baseUserNameById = baseUserDao.getBaseUserNameById(bpInspectRecord.getInspectUserId());
        resultDto.setInspectUserName(StringUtils.isEmpty(baseUserNameById) ? "" : baseUserNameById);
        //填报人员
        String fillFormUserName = baseUserDao.getBaseUserNameById(bpInspectRecord.getFillFormUserId());
        resultDto.setFillFormUserName(StringUtils.isEmpty(fillFormUserName) ? "" : fillFormUserName);
        //过期检查
        Integer expireFlag = bpInspectRecord.getExpireFlag();
        if (expireFlag == null || expireFlag == 0) {
            resultDto.setExpireCheck(null);
        }

        if (bpInspectPlan == null || CollectionUtils.isEmpty(bpInspectPlan.getPointList())) {
            return ResultBean.success(resultDto);
        }

        //巡检点
        List<BpInspectPlanCheckPoint> pointList = bpInspectPlan.getPointList();
        List<BpInspectRecordPointDto> resultPointDtoList = Lists.newArrayList();
        for (BpInspectPlanCheckPoint checkPoint : pointList) {
            BpInspectRecordPointDto recordPointDto = new BpInspectRecordPointDto();
            Long pointId = checkPoint.getPointId();
            BPInspectPointBean bpInspectPointBean = bpInspectPointService.getById(pointId);
            if (bpInspectPointBean == null) {
                continue;
            }
            //检查点名称
            recordPointDto.setInspectPointName(bpInspectPointBean.getInspectName());
            //检查开始/结束时间
            BpInspectRecordPoint recordPoint = bpInspectRecordPointRepository.findOneByInspectRecordIdAndInspectPointId(id, checkPoint.getPointId());
            if (recordPoint != null) {
                LocalDateTime inspectStartTime = recordPoint.getInspectStartTime();
                LocalDateTime inspectFinishTime = recordPoint.getInspectFinishTime();
                recordPointDto.setInspectStartTime(inspectStartTime);
                recordPointDto.setInspectFinishTime(inspectFinishTime);
                recordPointDto.setLocationException(recordPoint.getLocationException());
            }

            //检查设备集合
            List<BpInspectRecordPointDeviceDto> resultDeviceDtoList = Lists.newArrayList();
            List<BpInspectRecordDetail> detailList = bpInspectRecordDetailRepository.findByInspectRecordIdAndInspectPlanIdAndInspectPointId(
                    id, bpInspectPlan.getId(), checkPoint.getPointId());
            int itemSum = 0;
            String exceptionStatus = "正常";
            if (!CollectionUtils.isEmpty(detailList)) {
                Map<Long, List<BpInspectRecordDetail>> collect = detailList.stream().collect(Collectors.groupingBy(BpInspectRecordDetail::getInspectDeviceId));
                Map<Long, String> deviceMap = new HashMap<>(collect.size());
                for (Long aLong : collect.keySet()) {
                    BpInspectRecordPointDeviceDto bpInspectRecordPointDeviceDto = new BpInspectRecordPointDeviceDto();
                    bpInspectRecordPointDeviceDto.setId(aLong);
                    List<BpInspectRecordDetail> tempList = collect.get(aLong);
                    //设备名称
                    String deviceName = deviceMap.get(aLong);
                    if (deviceName == null) {
                        deviceName = bpInspectRecordDetailRepository.getDeviceNameById(tempList.get(0).getId());
                        deviceMap.put(aLong, deviceName);
                    }
                    //String deviceName = bpDeviceFacilityCollectionDao.findNameById(aLong);
                    bpInspectRecordPointDeviceDto.setDeviecName(deviceName);
                    List<BpInspectRecordPointDeviceDetailDto> dtos = tempList.stream().map(r -> {
                        BpInspectRecordPointDeviceDetailDto dto = new BpInspectRecordPointDeviceDetailDto();
                        BeanUtils.copyProperties(r, dto);
                        dto.setId(r.getId());
                        String inspectDevicePicture = r.getInspectDevicePicture();
                        if (inspectDevicePicture != null) {
                            List<String> pics = JSONArray.parseArray(inspectDevicePicture, String.class);
                            dto.setPics(pics);
                        }
                        return dto;
                    }).collect(Collectors.toList());
                    if (dtos != null) {
                        itemSum += dtos.size();
                        for (BpInspectRecordPointDeviceDetailDto dto : dtos) {
                            if ("异常".equals(dto.getInspectResultStatus())) {
                                exceptionStatus = "异常";
                            }
                        }
                    }
                    bpInspectRecordPointDeviceDto.setDeviceDetailDtoList(dtos);
                    resultDeviceDtoList.add(bpInspectRecordPointDeviceDto);
                }
            }
            //设备，项目总数
            recordPointDto.setDeviceSum(resultDeviceDtoList.size());
            recordPointDto.setItemSum(itemSum);
            //检查点是否异常 ：有一个异常即算异常
            recordPointDto.setExceptionStatus(exceptionStatus);
            recordPointDto.setDeviceDtoList(resultDeviceDtoList);
            resultPointDtoList.add(recordPointDto);
        }
        resultDto.setPointDtos(resultPointDtoList);
        return ResultBean.success(resultDto);
    }

    @Override
    public ResultBean exceptionHandle(Long id) {
        BpInspectRecord bpInspectRecord = bpInspectRecordRepository.findById(id).orElse(null);
        if (bpInspectRecord == null) {
            log.warn("检查记录表数据丢失，id:{}", id);
            return ResultBean.fail(101, "操作失败");
        }
        BpInspectRecordExceptionDto exceptionDto = new BpInspectRecordExceptionDto();
        String inspectPlanName = bpInspectPlanRepository.findNameById(bpInspectRecord.getInspectPlanId());
        exceptionDto.setInspectPlanName(inspectPlanName);
        String inspectUserName = baseUserDao.getBaseUserNameById(bpInspectRecord.getInspectUserId());
        exceptionDto.setInspectUserName(inspectUserName);
        exceptionDto.setInspectFinishTime(bpInspectRecord.getInspectFinishTime());
        //设备信息
        List<BpInspectRecordExceptionDetailDto> exceptionDetailDtoList = Lists.newArrayList();
        List<BpInspectRecordDetail> recordDetailList = bpInspectRecordDetailRepository.findByInspectRecordId(bpInspectRecord.getId());
        Map<Long, String> deviceMap = new HashMap<>();
        if (!CollectionUtils.isEmpty(recordDetailList)) {
            List<BpInspectRecordExceptionDetailDto> collect = recordDetailList.stream()
                    .filter(r -> "异常".equals(r.getInspectResultStatus()) ||
                            "未检".equals(r.getInspectResultStatus())).map(r -> {
                        BpInspectRecordExceptionDetailDto detailDto = new BpInspectRecordExceptionDetailDto();
                        BeanUtils.copyProperties(r, detailDto);
                        String deviceName = deviceMap.get(r.getInspectDeviceId());
                        if (deviceName == null) {
                            deviceName = bpInspectRecordDetailRepository.getDeviceNameById(r.getId());
                            deviceMap.put(r.getInspectDeviceId(), deviceName);
                        }
                        //设备名称
                        //String deviceName = bpDeviceFacilityCollectionDao.findNameById(r.getInspectDeviceId());
                        detailDto.setInspectDeviceName(deviceName);
                        detailDto.setCheckStatus(r.getInspectResultStatus());
                        String inspectDevicePicture = r.getInspectDevicePicture();
                        if (inspectDevicePicture != null && !"null".equals(inspectDevicePicture)) {
                            detailDto.setPics(JSON.parseArray(inspectDevicePicture, String.class));
                        }
                        return detailDto;
                    }).collect(Collectors.toList());
            exceptionDetailDtoList = collect;
        }
        exceptionDto.setExceptionDetailDtoList(exceptionDetailDtoList);
        return ResultBean.success(exceptionDto);
    }

    @Override
    public ResultBean getPlanDetailByPlanId(Long planId) {
        BpInspectPlan bpInspectPlan = bpInspectPlanService.getById(planId);
        if (bpInspectPlan == null) {
            log.warn("检查记录表数据丢失，id:{}", planId);
            return ResultBean.fail(101, "系统繁忙，请稍后重试");
        }
        List<BpInspectPlanCheckPoint> pointList = bpInspectPlan.getPointList();
        if (CollectionUtils.isEmpty(pointList)) {
            return ResultBean.success(Lists.newArrayList());
        }
        List<Long> pointIds = pointList.stream().map(r -> r.getPointId()).collect(Collectors.toList());
        List<BPInspectPointBean> resultList = Lists.newArrayList();
        for (Long pointId : pointIds) {
            BPInspectPointBean bpInspectPointBean = bpInspectPointService.getById(pointId);
            List<BPInspectPointDetailBean> details = bpInspectPointBean.getDetails();

            if (!CollectionUtils.isEmpty(details)) {
                for (BPInspectPointDetailBean detail : details) {
                    List<String> inspectItem = detail.getInspectItem();
                    if (!CollectionUtils.isEmpty(inspectItem)) {
                        List<String> strings = Stream.generate(() -> "").limit(inspectItem.size()).collect(Collectors.toList());
                        detail.setInspectResult(strings);
                        detail.setInspectResultStatus(strings);
                    }
                }
            }
            resultList.add(bpInspectPointBean);
        }
        return ResultBean.success(resultList);
    }

    /**
     * @param deviceId
     * @param flag     0 下达整改 ---改状态 整改中 ; 1 整改完成----改状态 整改完成   默认：未整改
     * @return
     */
    @Override
    public ResultBean repair(Long deviceId, int flag) {
        BpInspectRecordDetail detail = bpInspectRecordDetailRepository.findById(deviceId).orElse(null);
        if (detail == null) {
            log.warn("BpInspectRecordDetail数据丢失,id:{}", deviceId);
            return ResultBean.fail(101, "系统繁忙，稍后再试");
        }
        if (!"未整改".equals(detail.getRepairStatus())) {
            return ResultBean.fail(101, "当前状态不可整改");
        }
        detail.setRepairStatus("已处理");
//        if (flag == 0) {
//            detail.setRepairStatus("整改中");
//        } else if (flag == 1) {
//            detail.setRepairStatus("整改完成");
//        }
        bpInspectRecordDetailRepository.save(detail);
        return ResultBean.defaultSuccessResult();
    }

    @Override
    public ResultBean getRecordDetail(Long inspectRecordId, Long inspectPlanId, Long inspectPointId) {
        List<BpInspectRecordDetail> recordDetailList = bpInspectRecordDetailRepository.findByInspectRecordIdAndInspectPlanIdAndInspectPointId(inspectRecordId,
                inspectPlanId, inspectPointId);
        if (CollectionUtils.isEmpty(recordDetailList)) {
            return ResultBean.defaultSuccessResult();
        }
        Map<Long, List<BpInspectRecordDetail>> map = recordDetailList.stream().collect(Collectors.groupingBy(BpInspectRecordDetail::getInspectDeviceId));
        List<BpInspectPlanDeviceDto> deviceDtoList = Lists.newArrayList();
        Map<Long, String> deviceMap = new HashMap<>(map.size());
        for (Map.Entry<Long, List<BpInspectRecordDetail>> entry : map.entrySet()) {
            BpInspectPlanDeviceDto deviceDto = new BpInspectPlanDeviceDto();
            Long inspectDeviceId = entry.getKey();
            List<BpInspectRecordDetail> value = entry.getValue();
            deviceDto.setInspectDeviceId(inspectDeviceId);
            String deviceName = deviceMap.get(inspectDeviceId);
            if (deviceName == null) {
                BpInspectRecordDetail bpInspectRecordDetail = value.get(0);
                deviceName = bpInspectRecordDetailRepository.getDeviceNameById(bpInspectRecordDetail.getId());
                deviceMap.put(inspectDeviceId, deviceName);
            }
            //deviceDto.setInspectDeviceName(bpDeviceFacilityCollectionDao.findNameById(inspectDeviceId));
            deviceDto.setInspectDeviceName(deviceName);
            deviceDto.setDetailDtoList(value.stream().map(BpInspectPlanDeviceDetailDto::new).collect(Collectors.toList()));
            deviceDtoList.add(deviceDto);
        }
        return ResultBean.success(deviceDtoList);
    }

    @Override
    public ResultBean inspectExceptionQuery() {
        LocalDate endDate = LocalDate.now();
        LocalDate startDate = endDate.minusDays(29);
        List<BpInspectRecord> recordList = getRecordNdays(29);
        if (CollectionUtils.isEmpty(recordList)) {
            return ResultBean.success(EduExamReleaseDto.defaultDto());
        }
        //计算异常数量
        pageListHandle(recordList, false, false);
        Map<String, Long> map = recordList.stream().collect(Collectors.groupingBy((BpInspectRecord r) -> r.getCreateTime().format(DateTimeFormatter.ofPattern("yyyy-MM-dd"))
                , Collectors.summingLong(r -> r.getExceptionSum())));
        for (LocalDate temp = startDate; temp.isBefore(endDate.plusDays(1)); temp = temp.plusDays(1)) {
            String format = temp.format(DateTimeFormatter.ofPattern("yyyy-MM-dd"));
            map.putIfAbsent(format, 0L);
        }
        TreeMap<String, Long> treeMap = new TreeMap(map);
        return ResultBean.success(new EduExamReleaseDto(Lists.newArrayList(treeMap.keySet()), treeMap.values().stream().collect(Collectors.toList())));
    }

    @Override
    public ResultBean inspectExceptionRouteQuery() {
        List<BpInspectRecord> recordList = getRecordNdays(29);
        if (CollectionUtils.isEmpty(recordList)) {
            return ResultBean.success(EduExamReleaseDto.defaultDto());
        }
        //计算异常数量
        pageListHandle(recordList, false, false);
        Map<Long, Long> map = recordList.stream().collect(Collectors.groupingBy(BpInspectRecord::getInspectPlanId,
                Collectors.summingLong(r -> r.getExceptionSum())));
        List<Long> planIds = Lists.newArrayList(map.keySet());
        List<String> planNames = Lists.newArrayList();
        Map<Long, String> toolMap = bpInspectPlanRepository.findAllById(planIds).stream().collect(Collectors.toMap(BpInspectPlan::getId, BpInspectPlan::getName));
        for (Long id : planIds) {
            planNames.add(toolMap.get(id));
        }
        return ResultBean.success(new EduExamReleaseDto(planNames, map.values().stream().collect(Collectors.toList())));
    }

    /**
     * 获取N天内的巡检记录
     */
    private List<BpInspectRecord> getRecordNdays(int daysBefore) {
        LocalDateTime endTime = LocalDateTime.of(LocalDate.now(), LocalTime.of(23, 59, 59));
        LocalDateTime startTime = LocalDateTime.of(LocalDate.now().minusDays(daysBefore), LocalTime.of(0, 0, 0));
        List<BpInspectRecord> recordList = bpInspectRecordRepository.findByCreateTimeBetween(startTime, endTime);
        return recordList;
    }

    @Override
    public ResultBean inspectTodayFinish() {
        List<BpAppPlanTask> list = bpAppPlanTaskRepository.findByCheckDate(LocalDate.now());
        if (CollectionUtils.isEmpty(list)) {
            return ResultBean.success(BpInspectFinishRateDto.defaultFinishDto());
        }
        int size = list.size();
        Long finishSum = list.stream().filter(r -> "已完成".equals(r.getStatus())).count();
        int unFinishSum = size - finishSum.intValue();
        return ResultBean.success(BpInspectFinishRateDto.dataFinishDto(size, finishSum.intValue(), unFinishSum));
    }

    @Override
    public ResultBean inspectTodayException() {
        LocalDateTime startTime = LocalDateTime.of(LocalDate.now(), LocalTime.of(0, 0, 0));
        LocalDateTime endTime = LocalDateTime.of(LocalDate.now(), LocalTime.of(23, 59, 59));
        List<BpInspectRecord> list = bpInspectRecordRepository.findByCreateTimeBetween(startTime, endTime);
        if (CollectionUtils.isEmpty(list)) {
            return ResultBean.success(BpInspectFinishRateDto.defaultExceptionDto());
        }
        int size = list.size();
        Long normalSum = list.stream().filter(r -> r.getCheckResult() != null && r.getCheckResult().contains("正常")).count();
        int abnormalSum = size - normalSum.intValue();
        return ResultBean.success(BpInspectFinishRateDto.dataExceptionDto(size, normalSum.intValue(), abnormalSum));
    }

    @Override
    public ResultBean inspectFinishRate() {
        TreeMap<LocalDate, List<BpAppPlanTask>> treeMap = getLocalDateListTreeMap();
        Collection<List<BpAppPlanTask>> values = treeMap.values();
        List<Integer> inspectSumList = values.stream().map(r -> r.size()).collect(Collectors.toList());
        List<Long> finishList = values.stream().map(r -> r.stream().filter(v -> "已完成".equals(v.getStatus())).count()).collect(Collectors.toList());
        List<Integer> collect = finishList.stream().map(r -> r.intValue()).collect(Collectors.toList());
        return ResultBean.success(new BpInspectTaskFinishDto(Lists.newArrayList(treeMap.keySet()), collect, inspectSumList));
    }

    /**
     * 30天内的巡检任务
     */
    private TreeMap<LocalDate, List<BpAppPlanTask>> getLocalDateListTreeMap() {
        LocalDate endDate = LocalDate.now();
        LocalDate startDate = endDate.minusDays(29);
        List<BpAppPlanTask> list = bpAppPlanTaskRepository.findByCheckDateBetween(startDate, endDate);
        Map<LocalDate, List<BpAppPlanTask>> map = list.stream().collect(Collectors.groupingBy(BpAppPlanTask::getCheckDate));
        for (LocalDate temp = startDate; temp.isBefore(endDate.plusDays(1)); temp = temp.plusDays(1)) {
            map.putIfAbsent(temp, Lists.newArrayList());
        }
        return (TreeMap<LocalDate, List<BpAppPlanTask>>) new TreeMap(map);
    }

    @Override
    public ResultBean inspectUserQuery() {
        TreeMap<LocalDate, List<BpAppPlanTask>> treeMap = getLocalDateListTreeMap();
        Collection<List<BpAppPlanTask>> values = treeMap.values();
        List<Long> collect = values.stream().map(r -> r.stream().map(v -> v.getCheckUserId()).distinct().count()).collect(Collectors.toList());
        return ResultBean.success(new BpInpectUserDto(Lists.newArrayList(treeMap.keySet()), collect.stream().map(r -> r.intValue()).collect(Collectors.toList())));
    }

    @Override
    public ResultBean inspectExceptionPoint() {
        List<BpInspectRecord> recordList = getRecordNdays(29);
        if (CollectionUtils.isEmpty(recordList)) {
            return ResultBean.success(EduExamReleaseDto.defaultDto());
        }
        List<Long> recordIds = recordList.stream().map(r -> r.getId()).collect(Collectors.toList());
        List<BpInspectRecordDetailDto> recordDetailDtos = Lists.newArrayList();
        if (recordIds.size() > STACK_LIMIT) {
            ShiroUser currentUser = Context.getCurrentUser();
            int totalSize = (recordIds.size() / STACK_LIMIT_SPLIT) + 1;
            for (int j = 0; j < totalSize; j++) {
                List<Long> tempRecordIds = splitRecordIds(recordIds, totalSize, j);
                Future<?> submit = executor.submit(() -> {
                    Context.setCurrentUser(currentUser);
                    recordDetailDtos.addAll(bpInspectRecordDetailRepository.findPointIdAndResultByInspectRecordIdIn(tempRecordIds));
                });
                try {
                    submit.get();
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }
        //pointId -> resultStatus 异常
        Map<Long, Long> collect = recordDetailDtos.stream().filter(r -> !"正常".equals(r.getInspectResultStatus()))
                .collect(Collectors.groupingBy(BpInspectRecordDetailDto::getId, Collectors.counting()));
        //all
        Map<Long, Long> map = recordDetailDtos.stream().collect(Collectors.groupingBy(BpInspectRecordDetailDto::getId, Collectors.counting()));
        for (Long pointId : map.keySet()) {
            if (!collect.containsKey(pointId)) {
                map.put(pointId, 0L);
            } else {
                map.put(pointId, collect.get(pointId));
            }
        }
        ArrayList<Long> pointIds = Lists.newArrayList(map.keySet());
        Map<Long, String> stringMap = pointRepository.findAllById(pointIds).stream().collect(Collectors.toMap(BPInspectPoint::getId,
                BPInspectPoint::getInspectName));
        List<String> pointNameList = Lists.newArrayList();
        for (Long pointId : pointIds) {
            pointNameList.add(stringMap.get(pointId));
        }
        return ResultBean.success(new EduExamReleaseDto(pointNameList, map.values().stream().collect(Collectors.toList())));
    }

    /**
     * {
     * "floorId":1,
     * "points":[
     * {
     * "x":120.91926497508047,
     * "y":31.90738255454923,
     * "z":0.5,
     * "floorId":1
     * },
     * {
     * "x":120.92008758142714,
     * "y":31.908629072520895,
     * "z":0.5,
     * "floorId":1
     * },
     * {
     * "x":120.91993927805458,
     * "y":31.90674876129016,
     * "z":0.5,
     * "floorId":1
     * }
     * ]
     * }
     */
    @Override
    public ResultBean signIn(Long pointId) {
        Assert.notNull(pointId, "pointId cannot be null");
        Long currentUserId = Context.getCurrentUserId();
        return orientationDataSyncService.checkSnInArea(currentUserId, pointId);
    }


    @Override
    public void export(BpInspectRecordQueryBean bean, Integer flag, HttpServletRequest request, HttpServletResponse response) throws IOException {
        bean.setPage(0);
        bean.setSize(2000);
        Page<BpInspectRecord> page = list(bean, flag);
        List<BpInspectRecord> records = page.getContent();
        List<BpInspectExportBean> exportList = new ArrayList<>();
        records.forEach(each -> {
            BpInspectExportBean exportBean = new BpInspectExportBean();
            BeanUtils.copyProperties(each, exportBean);
            LocalDateTime inspectFinishTime = each.getInspectFinishTime();
            if (inspectFinishTime != null) {
                Date finishTimeDate = Date.from(inspectFinishTime.atZone(ZoneId.systemDefault()).toInstant());
                exportBean.setInspectFinishTime(DateUtil.formatDateTime(finishTimeDate));
            }
            LocalDateTime planStartTime = each.getPlanStartTime();
            if (planStartTime != null) {
                Date startTime = Date.from(planStartTime.atZone(ZoneId.systemDefault()).toInstant());
                exportBean.setPlanStartTime(DateUtil.formatDateTime(startTime));
            }
            exportList.add(exportBean);
        });
        OutputStream statOut = response.getOutputStream();
        ExcelWriter writer = ExcelUtil.getWriter(true);
        buildRecordExcelHeader(writer);
        writer.write(exportList, true);
        String userFileName = FileUtil.getEncodeFileName(request, "检查记录");
        response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet;charset=utf-8");
        response.setHeader("Content-Disposition", String.format("attachment;filename=%s", userFileName + ".xlsx"));
        writer.flush(statOut, true);
        writer.close();
        IoUtil.close(statOut);
    }


    private void buildRecordExcelHeader(ExcelWriter writer) {
        writer.addHeaderAlias("id", "检查记录编号");
        writer.addHeaderAlias("inspectPlanName", "检查路线");
        writer.addHeaderAlias("inspectType", "检查类型");
        writer.addHeaderAlias("inspectFinishTime", "检查完成时间");
        writer.addHeaderAlias("planStartTime", "计划开始时间");
        writer.addHeaderAlias("inspectUserName", "巡检人员");
        writer.addHeaderAlias("inspectTotalTime", "巡检用时");
        writer.addHeaderAlias("checkResult", "检查结果");
        writer.addHeaderAlias("exceptionHandle", "异常处理");
    }
}